#include "bluetooth_peripheral_service.h"

/**
 * @brief 读属性回调
 *
 * @param ConnHandle     - 连接句柄
 * @param pGattAttribute - 属性句柄
 * @param pValue         - 返回目标属性读取的值
 * @param pLengthOut     - 读取数据长度
 * @param Offset         - 读取偏移量
 * @return uint8_t       - 操作状态
 */
static uint8_t ProfileReadAttrCallback(uint16_t ConnHandle, gattAttribute_t *pGattAttribute, uint8_t *pValue, uint16_t *pLengthOut, uint16_t Offset, uint16_t MaxLen, uint8_t Method);

/**
 * @brief 写属性回调
 *
 * @param ConnHandle     - 连接句柄
 * @param pGattAttribute - 属性句柄
 * @param pValue         - 写入参数
 * @param Length         - 写入参数长度
 * @param Offset         - 写入偏移量
 * @return uint8_t       - 操作状态
 */
static uint8_t ProfileWriteAttrCallback(uint16_t ConnHandle, gattAttribute_t *pGattAttribute, uint8_t *pValue, uint16_t Length, uint16_t Offset, uint8_t Method);

/**
 * @brief 链接状态更改处理
 *
 * @param ConnHandle - 连接句柄
 * @param ChangeType - 修改类型
 */
static void ProfileHandleConnectStatusCallback(uint16_t ConnHandle, uint8_t ChangeType);

static ProfileWriteHandle_t ProfileWriteHandleCallback = NULL;

const uint8_t ProfileServUUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(PERIPHERAL_SERVICE_UUID),
    HI_UINT16(PERIPHERAL_SERVICE_UUID),
};

const uint8_t ProfilecharUUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(PERIPHERAL_CHARACTERISTIC_UUID),
    HI_UINT16(PERIPHERAL_CHARACTERISTIC_UUID),
};

static uint8_t ProfileCharUserDesp[] = "notify\0";
static uint8_t ProfileChar[PERIPHERAL_CHARACTERISTIC_LENGTH] = {0};
static uint8_t ProfileCharProps = GATT_PROP_NOTIFY | GATT_PROP_WRITE;
static gattCharCfg_t ProfileCharConfig[PERIPHERAL_MAX_CONNECTION] = {0};

static const gattAttrType_t ProfileServic = {ATT_BT_UUID_SIZE, ProfileServUUID};

static gattAttribute_t ProfileAttrTable[] = {
    {
        {ATT_BT_UUID_SIZE, primaryServiceUUID},
        GATT_PERMIT_READ,
        0,
        (uint8_t *)&ProfileServic,
    },

    {
        {ATT_BT_UUID_SIZE, characterUUID},
        GATT_PERMIT_READ,
        0,
        &ProfileCharProps,
    },

    {
        {ATT_BT_UUID_SIZE, ProfilecharUUID},
        GATT_PERMIT_WRITE,
        0,
        ProfileChar,
    },

    {
        {ATT_BT_UUID_SIZE, clientCharCfgUUID},
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        (uint8_t *)ProfileCharConfig,
    },

    {
        {ATT_BT_UUID_SIZE, charUserDescUUID},
        GATT_PERMIT_READ,
        0,
        ProfileCharUserDesp,
    },
};

gattServiceCBs_t ProfileCallback = {
    ProfileReadAttrCallback,
    ProfileWriteAttrCallback,
    NULL,
};

static uint8_t ProfileReadAttrCallback(uint16_t ConnHandle, gattAttribute_t *pGattAttribute, uint8_t *pValue, uint16_t *pLengthOut, uint16_t Offset, uint16_t MaxLen, uint8_t Method)
{
  uint8_t ReadAttrResult = SUCCESS;
  uint16_t UUID = BUILD_UINT16(pGattAttribute->type.uuid[0], pGattAttribute->type.uuid[1]);

  if (Offset > 0)
    return ATT_ERR_ATTR_NOT_LONG;

  if (pGattAttribute->type.len == ATT_BT_UUID_SIZE)
  {
    switch (UUID)
    {
    case PERIPHERAL_CHARACTERISTIC_UUID:
    {
      *pLengthOut = PERIPHERAL_CHARACTERISTIC_LENGTH;
      memcpy(pValue, pGattAttribute->pValue, PERIPHERAL_CHARACTERISTIC_LENGTH);
    }
    break;

    default:
    {
      *pLengthOut = 0;
      ReadAttrResult = ATT_ERR_ATTR_NOT_FOUND;
    }
    break;
    }
  }
  else
  {
    *pLengthOut = 0;
    ReadAttrResult = ATT_ERR_INVALID_HANDLE;
  }

  return ReadAttrResult;
}

static uint8_t ProfileWriteAttrCallback(uint16_t ConnHandle, gattAttribute_t *pGattAttribute, uint8_t *pValue, uint16_t LengthIn, uint16_t Offset, uint8_t Method)
{
  uint8_t WriteAttrResult = ATT_ERR_INVALID_HANDLE, NotifyApp = 0xFF;
  uint16_t UUID = BUILD_UINT16(pGattAttribute->type.uuid[0], pGattAttribute->type.uuid[1]);

  if (gattPermitAuthorWrite(pGattAttribute->permissions))
    return ATT_ERR_INSUFFICIENT_AUTHOR;

  if (pGattAttribute->type.len == ATT_BT_UUID_SIZE)
  {
    switch (UUID)
    {
    case GATT_CLIENT_CHAR_CFG_UUID:
    {
      WriteAttrResult = GATTServApp_ProcessCCCWriteReq(ConnHandle, pGattAttribute, pValue, LengthIn, Offset, GATT_CLIENT_CFG_NOTIFY);
    }
    break;

    case PERIPHERAL_CHARACTERISTIC_UUID:
    {
      WriteAttrResult = SUCCESS;

      if (ProfileWriteHandleCallback != NULL)
      {
        ProfileWriteHandleCallback(pValue, LengthIn);
      }
    }
    break;

    default:
    {
      WriteAttrResult = ATT_ERR_ATTR_NOT_FOUND;
    }
    break;
    }
  }

  return WriteAttrResult;
}

static void ProfileHandleConnectStatusCallback(uint16_t ConnHandle, uint8_t ChangeType)
{
  if (ConnHandle != LOOPBACK_CONNHANDLE)
  {
    if ((ChangeType == LINKDB_STATUS_UPDATE_REMOVED) || ((ChangeType == LINKDB_STATUS_UPDATE_STATEFLAGS) && (!linkDB_Up(ConnHandle))))
    {
      GATTServApp_InitCharCfg(ConnHandle, ProfileCharConfig);
    }
  }
}

uint8_t ProfileSetParameter(uint8_t Parameter, uint8_t Length, void *pDataIn)
{
  uint8_t SetResult = bleInvalidRange;

  switch (Parameter)
  {
  case PERIPHERAL_CHARACTERISTIC_ID:
  {
    if (Length == PERIPHERAL_CHARACTERISTIC_LENGTH)
    {
      SetResult = SUCCESS;
      tmos_memcpy(ProfileChar, pDataIn, PERIPHERAL_CHARACTERISTIC_LENGTH);
    }
  }
  break;

  default:
  {
    SetResult = INVALIDPARAMETER;
  }
  break;
  }

  return SetResult;
}

uint8_t ProfileGetParameter(uint8_t Parameter, void *pDataOut)
{
  uint8_t GetResult = SUCCESS;

  switch (Parameter)
  {
  case PERIPHERAL_CHARACTERISTIC_ID:
  {
    tmos_memcpy(pDataOut, ProfileChar, PERIPHERAL_CHARACTERISTIC_LENGTH);
  }
  break;

  default:
  {
    GetResult = INVALIDPARAMETER;
  }
  break;
  }

  return GetResult;
}

uint8_t ProfileNotify(uint16_t ConnHandle, attHandleValueNoti_t *pNotifyHandle)
{
  uint16_t value = GATTServApp_ReadCharCfg(ConnHandle, ProfileCharConfig);

  if (value & GATT_CLIENT_CFG_NOTIFY)
  {
    pNotifyHandle->handle = ProfileAttrTable[2].handle;
    return GATT_Notification(ConnHandle, pNotifyHandle, FALSE);
  }

  return bleIncorrectMode;
}

uint8_t ProfileAddService(ProfileWriteHandle_t WriteDataCallback)
{
  uint8_t AddResult = SUCCESS;

  ProfileWriteHandleCallback = WriteDataCallback;
  GATTServApp_InitCharCfg(INVALID_CONNHANDLE, ProfileCharConfig);
  linkDB_Register(ProfileHandleConnectStatusCallback);

  AddResult = GATTServApp_RegisterService(ProfileAttrTable,
                                          GATT_NUM_ATTRS(ProfileAttrTable),
                                          GATT_MAX_ENCRYPT_KEY_SIZE,
                                          &ProfileCallback);

  return AddResult;
}
